/*
 * HackSQL
 * Copyright (C) 2008-2009
 *
 * Authors: Higor Eurípedes Pimentel Fernandes de Araújo
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * $Id: main.c 57 2009-06-07 02:46:25Z heuripedes $
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <getopt.h>

#ifdef HAVE_LIBREADLINE
	#include <readline/readline.h>
	#include <readline/history.h>
#endif

/* Headers for networking. */
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

/* client headers */
#include "types.h"
#include "utils.h"
#include <hacksql/hacksql.h>
#include <hacksql/hs_hashtable.h>
#include <hacksql/libhacksql.h>


/* Some nasty prototypes */
void usage();
int parse_options(int argc, char **argv);

/* Control variables */
char *username = HACKSQL_DEFAULT_USERNAME;
char *password;
char *host = HACKSQL_DEFAULT_SERVER;
char *port = HACKSQL_DEFAULT_PORT;
int my_sock;

/* Some command handlers. */
int cmd_help(int argc, char **argv, char *raw);
int cmd_quit(int argc, char **argv, char *raw);
int cmd_run_sql(int argc, char **argv, char *raw);
int cmd_ed(int argc, char **argv, char *raw);
int cmd_ping(int argc, char **argv, char *raw);

command *find_cmd_by_name(const char *name);
int parse_input(char *input);
char *read_line (char *prompt);

command commands[] = {
	{"help", cmd_help, "Display this text."},
	{"quit", cmd_quit, "Quit HackSQL client."},
	{"ed", cmd_ed, "Edit a query string using $EDITOR."},
	{"ping", cmd_ping, "Sends a 'Ping' request to the server."},
	{NULL, NULL, NULL}
};

command sqlcommands[] = {
	{"select", cmd_run_sql, NULL},
	{"update", cmd_run_sql, NULL},
	{NULL, NULL, NULL}
};

/* Wheter to quit the main loop. */
int quit_hacksql = 0;

/* main() {{{ */
int main(int argc, char **argv) 
{
	char *line = NULL;

	/* Parse command line options */
	if (parse_options(argc, argv) == -1) {
		return -1;
	};

	hacksql_connect(host, port, username, password);
	
	printf("Welcome to the HackSQL client v%s, %s.\n", PACKAGE_VERSION, username);
	printf("Type 'help;' for help.\n\n");

	while (!quit_hacksql) {
		line = read_line("hacksql> ");

		if (!line) {
			continue;
		}

		parse_input(line);

		free(line);
		line = NULL;
	}

	hacksql_end();

	printf("Bye ;)\n");

	return EXIT_SUCCESS;
} /*}}}*/

/* parse_input() {{{ */
int parse_input(char *rawinput)
{
	command *cmd = NULL;
	char *input;
	char **tokens;
	int numokens, i, ret = 0;

	input = trimstr(rawinput);


	/* If its not a finished sequence, read a bit more and parse the input again. */
//	if (input[strlen(input) - 1] != ';') {
/*		char *line;
		line = read_line("       > ");
		input = (char*)realloc(input, sizeof(char) * (strlen(input) + strlen(line) + 2));
		input = strcat(input, " ");
		input = strcat(input, line);
		free(line);
	
		return parse_input(input);
		*/
//		fprintf(stderr, "HackSQL Error: missing ';' at end of line.\n");
//		return;
//8:	}

	tokens = tokenize(input, " ;", &numokens);

	if (!numokens || !tokens) {
		return 0;
	}

	if ((cmd = find_cmd_by_name(tokens[0])) != NULL) {
		ret = cmd->func(numokens, tokens, input);
	} else {
		/* maybe its an sql command? */
		ret = cmd_run_sql(numokens, tokens, input);
	}

	for (i = 0; i < numokens; i++) {
		free(tokens[i]);
	}

	free(tokens);

	return ret;
} /*}}}*/

/* find_cmd_by_name() {{{ */
command *find_cmd_by_name(const char *name)
{
	int i;

	for (i = 0; commands[i].name; i++) {
		if (strcmp(commands[i].name, name) == 0)
			return &commands[i];
	}

	return NULL;
} /*}}}*/

/* cmd_help() {{{ */
/*
 * Show the help text.
 */
int cmd_help(int argc, char **argv, char *raw)
{
	int i, j;
	int max_name = 0;

	for (i = 0; commands[i].name; i++) {
		if (strlen(commands[i].name) > max_name) {
			max_name = strlen(commands[i].name);
		}
	}

	for (i = 0; commands[i].name; i++) {
		printf("   %s", commands[i].name);

		for (j = strlen(commands[i].name); j < max_name; j++)
			printf("%s", " ");

		printf(" %s\n", commands[i].desc);
	}

	return 0;
} /*}}}*/

/* cmd_quit() {{{*/
/*
 * Quit HackSQL client.
 */
int cmd_quit(int argc, char **argv, char *raw)
{
	quit_hacksql = 1;
	hacksql_disconnect();
	
	return 0;
} /*}}}*/

/* cmd_ed {{{*/
/*
 * Opens the editor set in $EDITOR env variable to edit a sql query.
 */
int cmd_ed(int argc, char **argv, char *raw)
{
	char command[512];
	char *filename;
	char *tmpdir;
	char *editor;
	char *buffer;

	int fd;
	FILE *fp;
	long int fsize, readbytes;

	/* fetches the environment variables */
	hs_getenv(tmpdir, "TMP", "/tmp");
	hs_getenv(editor, "EDITOR", "vi");

	filename = (char *) malloc(sizeof(char) * (strlen(tmpdir) + sizeof("/hacksqlc-XXXXXX")));

	sprintf(filename, "%s/hacksql-XXXXXX", tmpdir);

	fd = mkstemp(filename);

	if (fd < 0) {
		hs_error(HS_ERROR_FILE_OPEN, "Cannot open temp file.");
		close(fd);
		remove(filename);
		free(filename);
		return 1;
	}

	sprintf(command, "%s %s", editor, filename);

	printf("Runing %s ...\n", command);

	if (system(command) != 0) {
		hs_warning("The editor returned a unexpected value.");
		remove(filename);
		free(filename);
		return 0;
	}

	fp = fdopen(fd, "w+");

	if (!fp) {
		hs_error(HS_ERROR_FILE_OPEN, "Cannot open temp file.");
		remove(filename);
		free(filename);
		return 1;
	}

	rewind(fp);
	fseek(fp, 0, SEEK_END);
	fsize = ftell(fp);
	rewind(fp);

	printf("Reading temp file...\n");

	buffer = (char *) malloc(sizeof(char) * (fsize + 1));

	readbytes = fread(buffer, 1, fsize, fp);

	if (readbytes >= 0)
		buffer[readbytes] = '\0';

	printf("Cleaning up...\n");
	fclose(fp);
	remove(filename);
	free(filename);
	free(buffer);

	return 0;

} /*}}}*/

/* cmd_ping() {{{ */

int cmd_ping(int argc, char **argv, char *raw)
{
	return hacksql_ping();
} /*}}}*/

/* cmd_run_sql() {{{ */
int cmd_run_sql(int argc, char **argv, char *raw)
{
	char *error;
	
	if (hacksql_query(raw) == FAILURE) {
		error = hacksql_error();
		printf("%s\n", error);		
		free(error);
	}

	return 1;
} /*}}}*/

/* parse_options() {{{ */
int parse_options(int argc, char **argv)
{
	int c;
	int long_index;
	int index;
	int has_error = 0;

	static struct option long_options[] = {
		{"user", required_argument, 0, 'u'},
		{"password", optional_argument, 0, 'p'},
		{"host", optional_argument, 0, 'h'},
		{"port", optional_argument, 0, 'P'},
		{"version", no_argument, 0, 'v'},
		{0, 0, 0, 0}
	};

	const char *short_options = "u:p::h::v";

	while ((c = getopt_long(argc, argv, short_options, long_options, &long_index)) != -1) {
		opterr = 0;	/* we don't want the annoying message */
		switch (c) {
		case 'u':
			username = optarg;
			break;
		case 'p':
			password = optarg;
			break;
		case 'h':
			host = optarg;
			break;
		case 'v':
			printf("%s\n", HACKSQL_VERSION);
			exit(0);
			break;
		case 'P':
			port = optarg;
			break;
		case '?':
			if (optopt == 'u') {
				printf(_("You have to provide an username\n"));
			} else {
				printf(_("Unknown option -%c\n"), optopt);
				usage();
			}
			has_error = -1;
			break;
		default:
			usage();
			has_error = -1;
		}

	}

	for (index = optind; index < argc; index++) {
		printf("Unknown option -%s\n", argv[index]);
		has_error = -1;
	}

	return has_error;

} /*}}}*/

/* read_line() {{{ */
char *read_line (char *prompt)
{
	char *buf;

#ifdef HAVE_LIBREADLINE
	buf = readline(prompt);
	add_history(buf);
#else
	char c = 0;
	int i = 0;

	buf = (char*)malloc(sizeof(char) * 2048);
	memset(buf, 0, 2048);

	printf("%s", prompt);
	
	while (c!= '\n' && i < 2047) {
		c = getchar();
		buf[i] = c;
		i++;
	}
	buf[i-1] = '\0';
	buf = (char*)realloc(buf, sizeof(char) * i);
#endif
	return buf;

} /*}}}*/

/* usage() {{{ */
void usage()
{
	printf(_("Usage: hacksql [options] \n"));
	printf(_("\t-u, --user \t\t The user to connect to the database\n"));
	printf(_("\t-p, --password \t\t The password for the user\n"));
	printf(_("\t-v, --version \t\t Displays version information\n"));
	printf(_("\t-h, --host \t\t Host where to connect to\n"));
	printf(_("\t-P, --port \t\t Remote port to connect to\n"));
} /*}}}*/

